import {
    getCurrentInstance,
    onActivated,
    onBeforeUnmount,
    onDeactivated,
    onMounted,
    watch,
    type Ref
} from 'vue';
import { useRoute } from 'vue-router';

type ShortcutHandler = () => void;
type ShortcutKey = string;
type ComponentId = string | symbol | number;

interface ShortcutBinding {
    key: ShortcutKey;
    handler: ShortcutHandler;
    componentId: ComponentId;
    priority: number;
}

interface ShortcutLayer {
    bindings: Map<ShortcutKey, ShortcutBinding>;
    componentId: ComponentId;
    priority: number;
}

// Global shortcut manager
class ShortcutManager {
    private layers: ShortcutLayer[] = [];
    private globalListener: ((e: KeyboardEvent) => void) | null = null;
    private currentRouteKey: string | null = null;

    constructor() {
        this.setupGlobalListener();
    }

    private setupGlobalListener() {
        this.globalListener = (e: KeyboardEvent) => {
            const key = this.generateKeyString(e);
            this.handleShortcut(key, e);
        };

        if (typeof window !== 'undefined') {
            window.addEventListener('keydown', this.globalListener, true);
        }
    }

    private generateKeyString(e: KeyboardEvent): string {
        const modifiers: string[] = [];

        // Collect modifiers in consistent order
        if (e.ctrlKey) modifiers.push('Ctrl');
        if (e.altKey) modifiers.push('Alt');
        if (e.shiftKey) modifiers.push('Shift');
        if (e.metaKey) modifiers.push('Meta');

        // Skip modifier keys when pressed alone
        if (e.key === 'Control' || e.key === 'Alt' || e.key === 'Shift' || e.key === 'Meta') {
            return '';
        }

        // Normalize key names for consistent binding
        let keyName = e.key;

        // Handle special cases and normalize
        const keyMappings: Record<string, string> = {
            ' ': 'Space',
            ArrowUp: 'ArrowUp',
            ArrowDown: 'ArrowDown',
            ArrowLeft: 'ArrowLeft',
            ArrowRight: 'ArrowRight',
            Enter: 'Enter',
            Escape: 'Escape',
            Tab: 'Tab',
            Backspace: 'Backspace',
            Delete: 'Delete',
            Insert: 'Insert',
            Home: 'Home',
            End: 'End',
            PageUp: 'PageUp',
            PageDown: 'PageDown'
        };

        // Use mapped key or original key
        keyName = keyMappings[keyName] || keyName;

        // Normalize the main key
        if (keyName.match(/^F\d+$/i)) {
            // Function keys: normalize to uppercase (F1, F2, etc.)
            keyName = keyName.toUpperCase();
        } else if (keyName.length === 1 && keyName.match(/[a-zA-Z]/)) {
            // Single letters: convert to lowercase for consistency
            keyName = keyName.toLowerCase();
        }

        const result = [...modifiers, keyName].join('+');
        return result;
    }

    private handleShortcut(key: ShortcutKey, e: KeyboardEvent) {
        // Find the highest priority layer that has this shortcut
        const activeLayer = this.getActiveLayer(key);

        if (activeLayer) {
            const binding = activeLayer.bindings.get(key);
            if (binding) {
                e.preventDefault();
                e.stopPropagation();
                binding.handler();
            }
        }
    }

    private getActiveLayer(key: ShortcutKey): ShortcutLayer | null {
        // Find the highest priority layer that actually has bindings
        const highestPriorityLayer = this.getHighestPriorityLayer();

        if (!highestPriorityLayer) {
            // No layers have bindings, check all layers for this specific key
            for (let i = this.layers.length - 1; i >= 0; i--) {
                const layer = this.layers[i];
                if (layer.bindings.has(key)) {
                    return layer;
                }
            }
            return null;
        }

        // Check if any layer with the highest priority (that has bindings) has this key
        const highestPriorityLayers = this.layers.filter(
            (layer) => layer.priority === highestPriorityLayer.priority && layer.bindings.size > 0
        );

        for (const layer of highestPriorityLayers) {
            if (layer.bindings.has(key)) {
                return layer;
            }
        }

        // If no layer with the highest priority has this key,
        // only block lower priority keys if the highest priority layer with bindings is above PAGE level
        if (highestPriorityLayer.priority > SHORTCUT_PRIORITY.PAGE) {
            return null;
        }

        // If highest priority is PAGE level or below, check all layers for this key
        for (let i = this.layers.length - 1; i >= 0; i--) {
            const layer = this.layers[i];
            if (layer.bindings.has(key)) {
                return layer;
            }
        }

        return null;
    }

    private getHighestPriorityLayer(): ShortcutLayer | null {
        if (this.layers.length === 0) {
            return null;
        }

        // Find the highest priority layer that actually has bindings
        for (let i = this.layers.length - 1; i >= 0; i--) {
            const layer = this.layers[i];
            if (layer.bindings.size > 0) {
                return layer;
            }
        }

        // If no layers have bindings, return null
        return null;
    }

    addLayer(componentId: ComponentId, priority: number = 0): ShortcutLayer {
        const layer: ShortcutLayer = {
            bindings: new Map(),
            componentId,
            priority
        };

        this.layers.push(layer);
        this.sortLayers();

        return layer;
    }

    removeLayer(componentId: ComponentId) {
        this.layers = this.layers.filter((layer) => layer.componentId !== componentId);
    }

    addBinding(layer: ShortcutLayer, key: ShortcutKey, handler: ShortcutHandler) {
        const binding: ShortcutBinding = {
            key,
            handler,
            componentId: layer.componentId,
            priority: layer.priority
        };

        layer.bindings.set(key, binding);
    }

    removeBinding(layer: ShortcutLayer, key: ShortcutKey) {
        layer.bindings.delete(key);
    }

    private sortLayers() {
        this.layers.sort((a, b) => a.priority - b.priority);
    }

    // Clean up shortcuts when route changes (for KeepAlive scenarios)
    clearRoute(routeKey: string) {
        if (this.currentRouteKey !== routeKey) {
            // Remove all page and component level shortcuts when switching routes
            this.layers = this.layers.filter((layer) => {
                // Only keep modal/dialog/popup layers (high priority) as they might be global
                return layer.priority >= SHORTCUT_PRIORITY.MODAL;
            });
        }
    }

    setCurrentRoute(routeKey: string) {
        this.currentRouteKey = routeKey;
    }

    // Clear all shortcuts for a specific route (called when route becomes inactive)
    clearCurrentRoute() {
        // Remove page and component level shortcuts for the current route
        this.layers = this.layers.filter((layer) => {
            return layer.priority >= SHORTCUT_PRIORITY.MODAL;
        });
    }

    destroy() {
        if (this.globalListener && typeof window !== 'undefined') {
            window.removeEventListener('keydown', this.globalListener, true);
        }
        this.layers = [];
    }
}

// Global instance
const shortcutManager = new ShortcutManager();

// Priority levels
export const SHORTCUT_PRIORITY = {
    PAGE: 0, // Base page shortcuts
    COMPONENT: 100, // Component level shortcuts
    MODAL: 1000, // Modal shortcuts (higher priority)
    DIALOG: 2000, // Dialog shortcuts (highest priority)
    POPUP: 3000 // Popup shortcuts (top level)
} as const;

/**
 * Normalize a shortcut key string to match runtime generation
 */
function normalizeShortcutKey(key: string): string {
    const parts = key.split('+').map((part) => part.trim());
    const modifiers: string[] = [];
    let mainKey = '';

    for (const part of parts) {
        const normalizedPart = part.charAt(0).toUpperCase() + part.slice(1).toLowerCase();
        if (
            normalizedPart === 'Ctrl' ||
            normalizedPart === 'Alt' ||
            normalizedPart === 'Shift' ||
            normalizedPart === 'Meta'
        ) {
            modifiers.push(normalizedPart);
        } else {
            mainKey = part;
        }
    }

    // Normalize the main key
    const keyMappings: Record<string, string> = {
        space: 'Space',
        enter: 'Enter',
        escape: 'Escape',
        esc: 'Escape',
        tab: 'Tab',
        backspace: 'Backspace',
        delete: 'Delete',
        insert: 'Insert',
        home: 'Home',
        end: 'End',
        pageup: 'PageUp',
        pagedown: 'PageDown',
        arrowup: 'ArrowUp',
        arrowdown: 'ArrowDown',
        arrowleft: 'ArrowLeft',
        arrowright: 'ArrowRight',
        up: 'ArrowUp',
        down: 'ArrowDown',
        left: 'ArrowLeft',
        right: 'ArrowRight'
    };

    // Check for mapped keys (case insensitive)
    const lowerMainKey = mainKey.toLowerCase();
    if (keyMappings[lowerMainKey]) {
        mainKey = keyMappings[lowerMainKey];
    } else if (mainKey.match(/^f\d+$/i)) {
        // Function keys: normalize to uppercase
        mainKey = mainKey.toUpperCase();
    } else if (mainKey.length === 1 && mainKey.match(/[a-zA-Z]/)) {
        // Single letters: convert to lowercase
        mainKey = mainKey.toLowerCase();
    }

    // Sort modifiers consistently: Ctrl, Alt, Shift, Meta
    const sortedModifiers = modifiers.sort((a, b) => {
        const order = ['Ctrl', 'Alt', 'Shift', 'Meta'];
        return order.indexOf(a) - order.indexOf(b);
    });

    return [...sortedModifiers, mainKey].join('+');
}

/**
 * Main hook for managing shortcuts in components
 */
export function useShortcut(priority: number = SHORTCUT_PRIORITY.PAGE) {
    const instance = getCurrentInstance();
    const route = useRoute();
    const componentId = instance?.uid ?? Symbol('shortcut-component');

    let layer: ShortcutLayer | null = null;
    const bindings = new Map<ShortcutKey, ShortcutHandler>();

    const createShortcutLayer = () => {
        if (!layer) {
            // Set current route for cleanup purposes
            shortcutManager.setCurrentRoute((route.name as string) || route.path);

            // Create layer for this component
            layer = shortcutManager.addLayer(componentId, priority);

            // Bind all pre-registered shortcuts
            for (const [key, handler] of bindings) {
                shortcutManager.addBinding(layer, key, handler);
            }
        }
    };

    const removeShortcutLayer = () => {
        if (layer) {
            shortcutManager.removeLayer(componentId);
            layer = null;
        }
    };

    onMounted(() => {
        createShortcutLayer();
    });

    // Handle KeepAlive scenarios
    onActivated(() => {
        createShortcutLayer();
    });

    onDeactivated(() => {
        removeShortcutLayer();
    });

    onBeforeUnmount(() => {
        removeShortcutLayer();
    });

    /**
     * Bind a shortcut key to a handler
     */
    function bind(key: ShortcutKey, handler: ShortcutHandler) {
        const normalizedKey = normalizeShortcutKey(key);
        bindings.set(normalizedKey, handler);

        // If layer is already created, bind immediately
        if (layer) {
            shortcutManager.addBinding(layer, normalizedKey, handler);
        }
        // If not, it will be bound when the component mounts
    }

    /**
     * Unbind a shortcut key
     */
    function unbind(key: ShortcutKey) {
        if (!layer) return;

        const normalizedKey = normalizeShortcutKey(key);
        bindings.delete(normalizedKey);
        shortcutManager.removeBinding(layer, normalizedKey);
    }

    /**
     * Unbind all shortcuts for this component
     */
    function unbindAll() {
        if (!layer) return;

        for (const key of bindings.keys()) {
            shortcutManager.removeBinding(layer, key);
        }
        bindings.clear();
    }

    /**
     * Check if a key is currently bound
     */
    function isBound(key: ShortcutKey): boolean {
        return bindings.has(key);
    }

    return {
        bind,
        unbind,
        unbindAll,
        isBound
    };
}

/**
 * Hook for dialog components that need the highest priority shortcuts
 */
export function usePageShortcut() {
    return useShortcut(SHORTCUT_PRIORITY.PAGE);
}

/**
 * Hook for modal components that need higher priority shortcuts
 */
export function useModalShortcut() {
    return useShortcut(SHORTCUT_PRIORITY.MODAL);
}

/**
 * Hook for dialog components that need the highest priority shortcuts
 */
export function useDialogShortcut() {
    return useShortcut(SHORTCUT_PRIORITY.DIALOG);
}

/**
 * Hook for popup components that need the highest priority shortcuts
 */
export function usePopupShortcut() {
    return useShortcut(SHORTCUT_PRIORITY.POPUP);
}

/**
 * Hook for managing shortcuts with reactive visibility control
 * Useful for modals, dialogs, etc. that can be shown/hidden
 */
export function useConditionalShortcut(
    visible: Ref<boolean>,
    priority: number = SHORTCUT_PRIORITY.MODAL
) {
    const instance = getCurrentInstance();
    const route = useRoute();
    // Create a unique component ID for each conditional shortcut instance
    const componentId = Symbol(`conditional-shortcut-${priority}-${Date.now()}-${Math.random()}`);

    let layer: ShortcutLayer | null = null;
    const bindings = new Map<ShortcutKey, ShortcutHandler>();

    // Watch visibility and manage layer accordingly
    const updateLayer = () => {
        if (visible.value && !layer) {
            // Create layer when visible
            shortcutManager.setCurrentRoute((route.name as string) || route.path);
            layer = shortcutManager.addLayer(componentId, priority);

            // Re-bind all existing shortcuts
            for (const [key, handler] of bindings) {
                shortcutManager.addBinding(layer, key, handler);
            }
        } else if (!visible.value && layer) {
            // Remove layer when hidden
            shortcutManager.removeLayer(componentId);
            layer = null;
        }
    };

    // Watch the visible ref for changes
    watch(
        visible,
        () => {
            updateLayer();
        },
        { immediate: true }
    );

    // Handle KeepAlive scenarios
    onActivated(() => {
        if (visible.value) {
            updateLayer();
        }
    });

    onDeactivated(() => {
        if (layer) {
            shortcutManager.removeLayer(componentId);
            layer = null;
        }
    });

    onBeforeUnmount(() => {
        if (layer) {
            shortcutManager.removeLayer(componentId);
        }
    });

    function bind(key: ShortcutKey, handler: ShortcutHandler) {
        const normalizedKey = normalizeShortcutKey(key);
        bindings.set(normalizedKey, handler);

        // If layer exists and is visible, bind immediately
        if (layer && visible.value) {
            shortcutManager.addBinding(layer, normalizedKey, handler);
        }
        // Otherwise, it will be bound when the layer is created/activated
    }

    function unbind(key: ShortcutKey) {
        const normalizedKey = normalizeShortcutKey(key);
        bindings.delete(normalizedKey);

        if (layer) {
            shortcutManager.removeBinding(layer, normalizedKey);
        }
    }

    function unbindAll() {
        if (layer) {
            for (const key of bindings.keys()) {
                shortcutManager.removeBinding(layer, key);
            }
        }
        bindings.clear();
    }

    return {
        bind,
        unbind,
        unbindAll,
        updateLayer
    };
}

// Export the manager for advanced usage
export { shortcutManager };
